package netty.socket;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.stream.JsonReader;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import netty.socket.*;
import netty.util.AESUtil;
import netty.util.MD5Helper;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import java.io.StringReader;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @Author zhangtao
 * @create 2021/11/16 17:59
 */
@Slf4j
public class SocketMqttHandler extends SimpleChannelInboundHandler<DatagramPacket> implements MqttCallback {

    private static final String KEY_HEAD = "AseaFfk";
    private MqttClient mClient;
    private MqttConnectOptions mOptions;
    public static String mac = "847C9BF8A233";
    private String nodeId = "Md00158D0005993D0F";

    public static final String MQTT_SOCKET_LOGIN = "/logging/add";

    public SocketMqttHandler(String host, String[] topic) {
        try {
            mClient = new MqttClient(host, "socketGateway", new MemoryPersistence());
            mOptions = new MqttConnectOptions();
            mOptions.setCleanSession(true);
            mOptions.setConnectionTimeout(100);
            mOptions.setKeepAliveInterval(1200);
            mClient.setCallback(this);
            mClient.connect(mOptions);
            mClient.subscribe(topic);
        } catch (Exception e) {
            log.info("init client error:" + e.getMessage());
        }
    }

    private String encode(Arg arg) {
        String key = MD5Helper.encrypt32(KEY_HEAD + mac + arg.getMsgid()).substring(0, 16).toLowerCase();
        return AESUtil.Encrypt(new Gson().toJson(arg), key);
    }

    private String decode(ResponseBean responseBean) {
        String key = MD5Helper.encrypt32(KEY_HEAD + mac + responseBean.getMsgid()).substring(0, 16).toLowerCase();
        return AESUtil.Decrypt(responseBean.getArg(), key);
    }

    @Override
    public void connectionLost(Throwable throwable) {

    }

    @Override
    public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
        ResponseBean responseBean = new Gson().fromJson(mqttMessage.toString(), ResponseBean.class);
        String arg = decode(responseBean);
        log.info("receive msg[{}]", arg);
        Arg argBean = new Gson().fromJson(arg, Arg.class);
        switch (argBean.getTopic()) {
            case "/subdevice/get_all_status":
                Arg rootBean = new Arg();
                rootBean.setMsgid(argBean.getMsgid());
                rootBean.setTopic("/subdevice/get_all_status");
                netty.socket.Data data = new netty.socket.Data();
                List<Nlist> nlistList = new ArrayList<>();
                Nlist nlist = new Nlist();
                nlist.setGuard(0);
                nlist.setNodeid(nodeId);
                nlist.setStatus(1);
                nlist.setVersion("");
                nlist.setModelid("");

                Endlist endlist = new Endlist();
                endlist.setEndpoint(1);
                Event event = new Event();
                event.setOnOff(1);
                event.setAlarm(15000);
                event.setPower(1000);
                event.setAlarm(0);
                event.setAccumulatedPower(10000);
                event.setRMSCurrent(5000);
                event.setRMSVoltage(22000);
                endlist.setEvent(event);

                List<Endlist> endlistList = new ArrayList<>();
                endlistList.add(endlist);
                nlist.setEndlist(endlistList);

                nlistList.add(nlist);
                data.setNlist(nlistList);


                rootBean.setData(data);

                ResponseBean response = new ResponseBean();
                response.setMsgid(argBean.getMsgid());
                response.setArg(encode(rootBean));
                sendMessage(new Gson().toJson(response).getBytes(StandardCharsets.UTF_8), "/pass_up/" + mac, 1);
                break;
            default:
                break;
        }
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {

    }

    public boolean sendMessage(byte[] messagePayload, String topic, int qos) {
        if (mClient != null) {
            try {
                mClient.getTopic(topic).publish(messagePayload, qos, false);
                return true;
            } catch (Exception e) {
                log.info("Failed to send outbound message (unexpected issue): " + new String(messagePayload));
                log.info(e.getLocalizedMessage());
                e.printStackTrace();
            }
        }
        return false;

    }

    @Override
    protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
        ByteBuf buf = msg.content();
        String strMsg = buf.toString(CharsetUtil.UTF_8);
        log.info("receive message[{}]", strMsg);
        JsonReader reader = new JsonReader(new StringReader(strMsg));
        //设置为宽松的解析方式
        reader.setLenient(true);
        JsonObject searchResponseBean = new Gson().fromJson(reader, JsonObject.class);
        String cmd = searchResponseBean.get("cmd").getAsString();
        if ("SearchHost".equals(cmd)) {
            SearchHostRes response = new SearchHostRes();
            response.setCmd("SearchHost");
            Arg arg = new Arg();
            arg.setDeviceid(mac);
            response.setArg(arg);

            InetSocketAddress remoteAddress = new InetSocketAddress("255.255.255.255", 49261);
            ByteBuf byteBuf = new UnpooledByteBufAllocator(false).buffer();
            byteBuf.writeBytes(new Gson().toJson(response).getBytes(StandardCharsets.UTF_8));
            DatagramPacket packet = new DatagramPacket(byteBuf, remoteAddress);
            ctx.channel().writeAndFlush(packet).sync();
        }

        ScheduledExecutorService schedule = Executors.newScheduledThreadPool(4);
        schedule.scheduleAtFixedRate(
                new Runnable() {
                    @Override
                    public void run() {
                        ResponseBean response = new ResponseBean();
                        Arg arg = new Arg();
                        arg.setMsgid(UUID.randomUUID().toString());
                        arg.setTopic("/logging/add");
                        Event event = new Event();
                        event.setOnOff(1);
                        event.setAlarm(15000);
                        event.setPower(1000);
                        event.setAlarm(0);
                        event.setRMSCurrent(1000);
                        event.setRMSVoltage(11000);
                        event.setAccumulatedPower(10000);
                        arg.setNodeid(nodeId);
                        Data data = new Data();
                        data.setEndpoint(1);
                        data.setEvent(event);
                        arg.setData(data);
                        response.setMsgid(arg.getMsgid());
                        response.setArg(encode(arg));
                        sendMessage(new Gson().toJson(response).getBytes(StandardCharsets.UTF_8), "/pass_up/" + mac, 1);
                    }
                }, 5, 10, TimeUnit.SECONDS);

    }
}
